package de.tu_darmstadt.gdi1.xcom.controller.ai;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import de.tu_darmstadt.gdi1.framework.utils.Point;
import de.tu_darmstadt.gdi1.xcom.controller.events.ActionEnum;
import de.tu_darmstadt.gdi1.xcom.controller.util.XcomLevelParser;
import de.tu_darmstadt.gdi1.xcom.model.elements.DirectionEnum;
import de.tu_darmstadt.gdi1.xcom.model.elements.Mob;

/**
 * Medium level AI.
 * Has an internal state, thus only one AI object
 * must be created per level.
 * 
 * Monsters act completely independent from each other.
 * Monsters have a "personality" that defines how
 * determined they are to reach a goal.
 *  
 * @author michael
 *
 */
public class XcomMediumAI implements XcomAIInterface {

	private XcomLevelParser parser;
	private XcomAIGame aiGame;
	
	private ArrayList<MonsterBrain> brains = new ArrayList<MonsterBrain>();
	
	private HashMap<Point, MonsterBrain> brainLocation = new HashMap<Point, MonsterBrain>();
	
	Random rand = new Random();
	
	//final String ENDTURN = "" + 0 + ";" + 0 + ";" + ActionEnum.EndTurn.getChar();
	
	/*
	private String makeTurn(){
		double test = Math.random();
		List<Mob> mobs = aiGame.getActiveTeamMobs();
		Mob currentMob = selectMob(mobs);
		ActionEnum ae = null;
		if(test < 0.5 && currentMob.getPoints() > 3){
			ae = ActionEnum.Move;
		}if(test >= 0.5 && test < 0.75 && currentMob.getPoints() > 1){
			ae = ActionEnum.RotateLeft;
		}if(test >= 0.75 && test < 1.0 && currentMob.getPoints() > 1){
			ae = ActionEnum.RotateRight;
		}
		if(ae == null)
			ae = ActionEnum.EndTurn;
		Point pos = currentMob.getPosition();
		return "" + pos.getX() + ";" + pos.getY() + ";" + ae.getChar();
	}
	private Mob selectMob(List<Mob> mobs) {
		double test = Math.random();
		int index = (int) Math.round(test * (mobs.size() - 1));
		return mobs.get(index);
	}
*/

	@Override
	public String getMove(String game) {
		if(parser == null)
			parser = new XcomLevelParser(game);
		else
			parser.setLevel(game);
		
		// update game
		aiGame = parser.parse();
		
		// if not my turn: done
		if(!aiGame.getActiveTeam().isUseAI()) {
			System.err.println("Bad call: Don't call if it's not the AI's turn!");
			return "" + brains.get(0).getMonster().getPosition().getX() + ";" + brains.get(0).getMonster().getPosition().getY() + ";" + ActionEnum.EndTurn.getChar();//ENDTURN;//null;
		}
		
		// tell all brains about the new situation
		for(MonsterBrain mb : brains) {
			mb.updateGame(aiGame);
		}
		
		// sort out dead brains
		Iterator<MonsterBrain> iter = brains.iterator();
		while(iter.hasNext()) { 
			MonsterBrain b = iter.next();
			if(b.isDead()) {
				brainLocation.remove(b.getPosition());
				iter.remove();
			}
		}
		

		
		// create Brains for new monsters
		List<Mob> monsters = aiGame.getActiveTeamMobs();
		for(Mob m : monsters) {
			Point mpos = m.getPosition();
			if(!brainLocation.containsKey(mpos)){
				MonsterBrain newBrain = new MonsterBrain(mpos,aiGame);
				brains.add(newBrain);
				brainLocation.put(mpos, newBrain);
			}
		}
		
		// choose one to make a move, based on weighted probability.
		// The following code is crazy, but I don't have the time
		// to figure out how to do this the "proper" way.
		ArrayList<MonsterBrain> candidates = new ArrayList<MonsterBrain>();
		for(MonsterBrain mb: brains) {
			if(mb.isAlive() && (!mb.isIdle()) && (mb.getMonster().getPoints()>=3)) { // skip dead monsters. Is this even necessary? FIXME
				for(int i = 0; i < mb.getMonster().getPoints(); i++) {
					candidates.add(mb);
				}
			}
		}
		
		// are there actually any monsters who still have action points left?
		// if not, end turn.
		if(candidates.size() == 0) {
			for(MonsterBrain mb: brains)
				mb.setIdle(false);
			return "" + brains.get(0).getMonster().getPosition().getX() + ";" + brains.get(0).getMonster().getPosition().getY() + ";" + ActionEnum.EndTurn.getChar();//ENDTURN;//null;
		}
		
		// pick a brain at weighted random
		MonsterBrain activeBrain = candidates.get(rand.nextInt(candidates.size()));
		
		ActionEnum action = activeBrain.doSomething(aiGame);
		
		if(action == null)
			action = ActionEnum.EndTurn;
		
		Point pos = activeBrain.getPosition();
		
		// Monster wants to move? Is that possible here?
		if(action == ActionEnum.Move) {
			Point targetPosition = getNeighbourPoint(pos, activeBrain.getDir());
			if(aiGame.getElementsAt(targetPosition).get(0).isSolid()) { // something in the way
				System.out.println("Plan is impossible, make new one");
				// plan is impossible. Make a new one.
				activeBrain.makeNewPlan();
				// try again:
				return getMove(game);
			}
			// else: move brain around
			brainLocation.remove(activeBrain.getPosition());
			brainLocation.put(targetPosition, activeBrain);
			activeBrain.position = targetPosition;
		}
		System.out.println("Perform action "+action+" at "+pos);
		return "" + pos.getX() + ";" + pos.getY() + ";" + action.getChar();
	}
	
	Point getNeighbourPoint(Point p, DirectionEnum side) {
		switch(side) {
		case East:	return Point.modifyXAxis(p, +1);
		case North: return Point.modifyYAxis(p, -1);
		case South: return Point.modifyYAxis(p, +1);
		case West:  return Point.modifyXAxis(p, -1);
		}
		
		// unreachable code:
		return p;
	}
}
